<?php
/**
 * @package qBase
 * @subpackage web
 */

/**
 * QWebApp represents a project and can contain modules, listeners or web pages
 * 
 * @package qBase
 * @subpackage web
 *  
 */
class QWebApp extends QWebModule 
{
	/**
	 * The cleanup interval in seconds
	 * The default is 300 (10 minutes)
	 *
	 * @var integer
	 */
	public static $ClenupInterval = 300;
	/**
	 * Delete a JS stream file if older than N seconds
	 * The default is 120 (2 minutes)
	 *
	 * @var integer
	 */
	public static $ClenupJsStreamsAfter = 120;
	/**
	 * Set it to true if cleanup is needed
	 *
	 * @var boolean
	 */
	public static $ClenupNeeded = false;
	/**
	 * The full path to the folder that contains the script that started the PHP execution
	 *
	 * @var string
	 */
	private static $RunningFolder;
	
	/**
	 * The root document of the web server of virtual web server as proviede by PHP : $_SERVER['DOCUMENT_ROOT']
	 *
	 * @var string
	 */
	private static $DocumentRoot;
	/**
	 * The realtive path to the folder that contains the script that started the PHP execution
	 *
	 * @var string
	 */
	private static $RelativeRunningFolder;
	/**
	 * The relative path from the application to the framework, usually is : "../qBase/"
	 *
	 * @var string
	 */
	private static $FrameworkRelativePath;
	/**
	 * The full path to the temp folder
	 *
	 * @var string
	 */
	private static $TempPath;
	/**
	 * The relative path to the temp folder
	 *
	 * @var string
	 */
	private static $TempPathRelative;
	
	/**
	 * The default QWebApp
	 *
	 * @var QWebApp
	 */
	protected static $Default;
	/**
	 * The default resource path for QWebControl(s)
	 *
	 * @var string
	 */
	protected static $DefaultResPath = null;
	/**
	 * The debug text
	 *
	 * @var array
	 */
	protected static $DebugData = null;
	/**
	 * If in debug mode will display debug messages
	 *
	 * @var boolean
	 */
	protected static $DebugMode = true;
	
	/**
	 * The default constructor of the QWebApp object
	 *
	 */
	public function __construct()
	{
		parent::__construct();
	}

	/**
	 * Creates the default QWebApp object based on the class name provided
	 * class name must be an inheriting class of QWebApp
	 *
	 * @param string $class_name
	 */
	final public static function Init($class_name)
	{
		if(get_magic_quotes_gpc())
			throw new Exception("Magic quotes are on please disable them");
		// VERY IMPORTANT ! SECURITY RELATED
		if (!QObject::IsA($class_name, "QWebApp"))
			throw new Exception("QWebApp::Init() the specified class name must inherit QWebApp");
		eval("$class_name::\$Default = new {$class_name}();");
	}

	/**
	 * The execution point for the web application
	 *
	 */
	final public static function Run()
	{
		// create context 
		self::$RunningFolder = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), "/\\ \t\r\n") . "/";
		self::$RelativeRunningFolder = "/" . trim(dirname($_SERVER['PHP_SELF']), "/\\ \t\r\n") . "/";
		
		self::$DocumentRoot = rtrim($_SERVER['DOCUMENT_ROOT'], "/\\ \t\r\n") . "/";
		
		self::$FrameworkRelativePath = self::RelativePath(__QBasePath);
		
		if (!self::GetTempPath())
			throw new Exception("No TEMP folder was found (".self::$TempPath.")");
			
		if (!is_dir(self::GetTempPath()))
			mkdir(self::GetTempPath(), 0755, true);
			
		if ((!self::$Default->base_href) && defined("__BASE_HREF"))
			self::$Default->base_href = __BASE_HREF;
		
		if ((!self::$Default->request_url) && isset($_GET["__original_request__"]))
		{
			$_GET["__original_request__"] = trim($_GET["__original_request__"], "/\\ ");
			self::$Default->request_url = $_GET["__original_request__"];
			
			// var_dump($_GET["__original_request__"]);
		}
		
		// self::$Default->debug($_GET, $_POST);

		try
		{
			if ((isset($_GET["___q"]) || (isset($_POST["___q"]))) && self::$Default->getUrlManager())
			{
				// note that we load from the hash and do not update the hash
				// self::debugData("#");
				self::$Default->getUrlManager()->updateCallStackAndEventsFromUrl((isset($_GET["___q"]) ? $_GET["___q"] : $_POST["___q"]), false);
			}
			else if (self::$Default->getUrlManager())
			{
				// self::debugData("no #");
				self::$Default->getUrlManager()->updateCallStackAndEventsFromUrl(self::$Default->request_url);
			}
		}
		catch (QWebException404 $ex)
		{
			header("HTTP/1.0 404 Not Found");
			die("404 Not Found");
		}
		
		// 	
		
		// call beforeRun
		self::$Default->beforeRun();

		// run the listener(s)
		QWebContext::CreateContext();
		
		$current_call_stack = null;
		while ($current_call_stack = QWebContext::GetCurrentCallStack())
		{
			if (QObject::IsA($current_call_stack, "QWebApp"))
				QWebContext::IncrementCallStackPosition();
			else 
				break;
		}
		// self::$Default->debug("CCS : " . $current_call_stack);
		
		self::$Default->processRequest(QWebContext::Request());

		// it's done 
		// call afterRun
		self::$Default->afterRun();

		// send headers & output to the browser
		self::$Default->sendResponse();
	}
	
	/**
	 * Sets the temp folder for the application
	 *
	 * @param string $path
	 */
	final public static function SetTempPath($path)
	{
		self::$TempPath	= rtrim($path, "\\/ \t\n\r") . "/";
		self::$TempPathRelative = self::RelativePath(self::$TempPath);
	}
	
	/**
	 * Gets the temp folder of the application
	 *
	 * @return string
	 */
	final public static function GetTempPath()
	{
		return self::$TempPath;
	}
	/**
	 * Gets the relative path to the temp folder of the application
	 *
	 * @return string
	 */
	final public static function GetRelativeTempPath()
	{
		return self::$TempPathRelative;
	}

	/**
	 * sends the response to the browser
	 *
	 */
	final private function sendResponse()
	{
		// send the response to the browser
		QWebContext::Response()->send();
	}
	
	final public static function DoCleanup()
	{
		if (self::$ClenupNeeded)
		{
			$now = time();
			$js_save_path = self::GetRunningFolder() . QWebApp::GetRelativeTempPath() . "js_streams/";
			// /var/www/qBaseRomguide/temp/js_streams
			// $js_save_path = "temp/js_streams/";

			// echo "DoCleanup : {$js_save_path}<br>";
			// var_dump(is_dir($js_save_path));
			if (is_dir($js_save_path))
			{
				$items = scandir($js_save_path);
				foreach ($items as $itm)
				{
					$path = $js_save_path . $itm;
					// echo $path."<br>";
					if (is_file($path))
					{
						$mtime = filemtime($path);
						if (($now - $mtime) >= self::$ClenupJsStreamsAfter)
						{
							// echo "delete: $path <br>";
							unlink($path);
						}
					}
				}
			}
		}
	}
	
	/**
	 * Empty in QWebApp, it is executed before Run
	 *
	 */
	protected function beforeRun()
	{
		
	}
	
	/**
	 * Converts the full path of any file on the server relative to the specified folder 
	 * or (if not specified) relative to the executing script (self::$RunningFolder)
	 *
	 * @param string $path
	 * @param string $relative_to
	 * @return string
	 */
	final public static function RelativePath($path, $relative_to = null)
	{
		$rel_path = str_replace("\\", "/", $path);

		if (is_null($relative_to))
			$relative_to = self::$RunningFolder;

		if (strlen(trim($relative_to, "/\\ \t\r\n")) == 0)
			return $rel_path;

		$root_parts = explode("/" , trim($relative_to, "/\\ \t\r\n"));
		
		// full path
		$path_parts = explode("/" , trim($rel_path, "/\\ \t\r\n"));

		$pos = 0;
		$c_path_parts = count($path_parts);
		$c_root_parts = count($root_parts);
		$min = $c_path_parts > $c_root_parts ? $c_root_parts : $c_path_parts;
		
		for ($i = 0; $i < $min; $i++)
		{
			if ($path_parts[$i] == $root_parts[$i])
				$pos++;
			else 
				break;
		}
		
		$ends_slash = (substr($rel_path, strlen($rel_path) - 1) == '/');
		// nr of common parts = $pos
		$rell = "";
		for ($i = $pos; $i < $c_root_parts; $i++)
		{
			$rell .= "../";
		}
		for ($i = $pos; $i < $c_path_parts; $i++)
		{
			$rell .= $path_parts[$i];
			if (($i < ($c_path_parts - 1)) || $ends_slash)
				$rell .= "/";
		}
		return $rell;
	}
	
	/**
	 * Gets the relative path of a file in the framework, it's relative to $RunningFolder
	 *
	 * @param string $path
	 * @return string
	 */
	final public static function FramewordRelativePath($path)
	{
		return rtrim(self::$FrameworkRelativePath, "/\\ \t\r\n") . "/" . ltrim($path, "/\\ \t\r\n");
	}
	
	/**
	 * Alias for FramewordRelativePath
	 *
	 * @param string $path
	 * @return string
	 */
	final public static function FwRel($path)
	{
		return self::FramewordRelativePath($path);
	}
	
	/**
	 * Empty in QWebApp, it is executed after Run
	 *
	 */
	protected function afterRun()
	{
		
	}
	
	/**
	 * Gets the folder of the executing script
	 *
	 * @return string
	 */
	public static function GetRunningFolder()
	{
		return self::$RunningFolder;
	}
	
	/**
	 * Gets the executing script
	 *
	 * @return string
	 */
	public static function GetRunningScript()
	{
		return $_SERVER['SCRIPT_FILENAME'];
	}

	/**
	 * Adds a module to the application, this function is an alias for AddWebListener
	 *
	 * @param string $class_name
	 */
	public static function AddModule($class_name)
	{
		self::$Default->addListener($class_name);
	}

	/**
	 * Removes a module from the application, this function is an alias for RemoveWebListener
	 *
	 * @param string $class_name
	 */
	public static function RemoveModule($class_name)
	{
		self::$Default->removeListener($class_name);
	}
	
	/**
	 * Gets the path of the framework
	 *
	 * @return string
	 */
	public static function GetFrameworkPath()
	{
		return __QBasePath;
	}

	/**
	 * Adds a web listener to the application 
	 *
	 * @param string $class_name
	 */
	public static function AddWebListener($class_name, $set_default = false, $parameters = null)
	{
		self::$Default->addListener($class_name, $set_default, $parameters);
	}
	/**
	 * Removes a web listener from the application 
	 *
	 * @param string $class_name
	 */
	public static function RemoveWebListener($class_name)
	{
		self::$Default->removeListener($class_name);
	}
	
	/**
	 * Clears all the listeners of the application , web modules included
	 *
	 */
	public static function RemoveAllWebListeners()
	{
		self::$Default->removeAllListeners();
	}
	
	/**
	 * Checks if this application contains the specified WebListener, this function will not recurse
	 *
	 * @param string $class_name
	 */
	public static function HasWebListener($class_name)
	{
		self::$Default->hasListener($class_name);
	}
	
	/**
	 * Gets the default web listener
	 *
	 * @return QIWebListener
	 */
	public function GetDefaultWebListener()
	{
		return self::$Default->getDefaultListener();
	}
	
	/**
	 * Sets the default listener as class name
	 *
	 * @param string $listener The class name of the listener to be the default IWebListener for the application
	 */
	public function SetDefaultWebListener($listener)
	{
		self::$Default->setDefaultListener($listener);
	}
	
	/**
	 * Gets the running page 
	 *
	 * @return QWebPage
	 */
	public function GetWebPage()
	{
		return self::$Default->getRunningPage();
	}
	
	/**
	 * Sets the default resource path for QWebControl(s)
	 *
	 * @param string $path
	 */
	public function SetDefaultResPath($path)
	{
		self::$Default->setDefaultResourcePath($path);
	}
	
	/**
	 * Gets the default resource path for QWebControl(s)
	 *
	 * @return string
	 */
	public function GetDefaultResPath()
	{
		return self::$Default->getDefaultResourcePath();
	}
	/**
	 * Appends a debug text to the debug data
	 *
	 * @param string $text
	 */
	public static function DebugAppend($text)
	{
		if (!self::$DebugData)
			self::$DebugData = array();

		self::$DebugData[] = $text;
	}
	
	/**
	 * Gets the debug data array
	 *
	 * @return array
	 */
	public static function GetDebugData()
	{
		return self::$DebugData;
	}
	
	/**
	 * It will get the dump using the PHP function var_dump 
	 * for any variables passed as parameters 
	 *
	 */
	public static function DebugVarDump()
	{
		if (func_num_args() > 0)
		{
			$args = func_get_args();
			ob_start();
			call_user_func_array("var_dump", $args);
		
			if (!self::$DebugData)
				self::$DebugData = array();	
			self::$DebugData[] = ob_get_clean();
		}
		else 
		{
			throw new Exception("QWebApp::DebugVarDump invalid parameters");
		}
	}
	
	/**
	 * Gets the Url manager of the module
	 *
	 * @return QUrlManager
	 */
	public static function GetDefaultUrlManager()
	{
		return self::$Default->getUrlManager();
	}
	
	/**
	 * Sets the Url manager of the module
	 *
	 * @param QUrlManager $url_manager
	 */
	public static function SetDefaultUrlManager(QUrlManager $url_manager)
	{
		// self::$Default->debug(get_class(self::$Default));
		self::$Default->setUrlManager($url_manager);
	}
	
	public static function EnableDebugMode()
	{
		self::$DebugMode = true;
	}
	
	public static function DisableDebugMode()
	{
		self::$DebugMode = false;
	}
	
	public static function InDebugMode()
	{
		return self::$DebugMode;
	}
	
	public static function GetCallStackLink($call_stack)
	{
		$count_call_stack = count($call_stack);
		
		$url = "";
		for ($i = 0; $i < $count_call_stack; $i++)
		{
			if ($i == 0) 
				$url .= "?";
			else 
				$url .= "&";
				
			$url .= "__qwl{$i}=".urlencode($call_stack[$i]);
		}

		return $url;
	}
}

?>